K3RN3LCTF Recurso Write Up
Details:
Jeopardy style CTF
Category: Reverse Engineering
Write up:
Opening the binary in a decompiler we can see that we are lucky and the binary wasn't stripped which is awesome. So we start by taking a look at the main function:
int __cdecl main(int argc, const char **argv, const char **envp)
{
if ( argc > 1 && strstr(argv[1], ".recc") )
{
v7 = (unsigned int *)initBytecodeFileWithFile(argv[1]);
runProgram(*((_QWORD *)v7 + 2), *((_QWORD *)v7 + 3), *v7);
}
else if ( argc > 1 && strstr(argv[1], ".rec") )
{
fileData = (void *)readFile(argv[1], &fileSize);
v5 = fileData;
program = initProgramNode();
v6 = initFunctionNode("main");
addElementToProgramNode(program, v6);
curFunction = v6;
parseFunction(program, &v5);
if ( argc == 4 && !strcmp(argv[2], "-s") )
v3 = argv[3];
else
v3 = (const char *)&unk_5008;
compileBytecode(program, v3);
free(fileData);
freeProgramNode(program);
}
return 0;
}
The file we were given is a .recc file which means we can ignore the else if statement and dive straight into the if statement. Since we check if .recc is in argv[1] we can assume that that is our file name and we pass it into initBytecodeFileWithFile:
int *__fastcall initBytecodeFileWithFile(const char *a1)
{
v3 = (int *)malloc(0x20uLL);
stream = fopen(a1, "rb");
fread(v3 + 2, 4uLL, 1uLL, stream);
*((_QWORD *)v3 + 2) = malloc(v3[2] + 1);
fread(*((void **)v3 + 2), 1uLL, v3[2], stream);
v3[1] = 1024;
*((_QWORD *)v3 + 3) = malloc(v3[1] + 1);
for ( i = fread(*((void **)v3 + 3), 1uLL, 0x400uLL, stream);
i == 1024;
i = fread((void *)(*((_QWORD *)v3 + 3) + *v3), 1uLL, 0x400uLL, stream) )
{
*v3 += 1024;
v3[1] += 1024;
*((_QWORD *)v3 + 3) = realloc(*((void **)v3 + 3), v3[1] + 1);
}
*v3 += i;
fclose(stream);
return v3;
}
The first thing that we do is malloc some space and then open the file and read 4 bytes, the size of an int. We then malloc the amount of memory that we just read from the first 4 bytes and read that amount of data. The rest of the function seems to start by mallocing some memory and then reading in the data, reallocating more memory if needed.
We then go into the next function:
__int64 __fastcall runProgram(__int64 a1, __int64 a2)
{
v76 = initializeFunctions(a1);
v84 = 0;
v83 = 0;
v82 = 0;
v81 = 0;
v80 = 64;
ptr = malloc(0x200uLL);
for ( i = 0; i < v80; ++i )
*((_QWORD *)ptr + i) = malloc(8uLL);
while ( 1 )
{
result = *(unsigned __int8 *)(v84 + a2);
if ( (_BYTE)result == 13 )
return result;
v2 = v84++;
v75 = *(_BYTE *)(v2 + a2);
switch ( v75 )
{
case 1:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v3 = v83++;
*(_QWORD *)&v73[8 * v3 + 8] = *((_QWORD *)&v74 + 1) - v74;
break;
case 2:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v4 = v83++;
*(_QWORD *)&v73[8 * v4 + 8] = v74 * *((_QWORD *)&v74 + 1);
break;
case 3:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v6 = v83++;
*(_QWORD *)&v73[8 * v6 + 8] = *((_QWORD *)&v74 + 1) + v74;
break;
case 8:
v10 = v84++;
v73[0] = *(_BYTE *)(v10 + a2);
v11 = v84++;
v73[1] = *(_BYTE *)(v11 + a2);
v12 = v84++;
v73[2] = *(_BYTE *)(v12 + a2);
v13 = v84++;
v73[3] = *(_BYTE *)(v13 + a2);
v14 = v84++;
v73[4] = *(_BYTE *)(v14 + a2);
v15 = v84++;
v73[5] = *(_BYTE *)(v15 + a2);
v16 = v84++;
v73[6] = *(_BYTE *)(v16 + a2);
v17 = v84++;
v73[7] = *(_BYTE *)(v17 + a2);
v18 = v83++;
*(_QWORD *)&v73[8 * v18 + 8] = bytesToLongLong(v73);
break;
case 11:
v19 = v84++;
v69 = *(_BYTE *)(v19 + a2);
v20 = v84++;
v70 = *(_BYTE *)(v20 + a2);
v21 = v84++;
v71 = *(_BYTE *)(v21 + a2);
v22 = v84++;
v72 = *(_BYTE *)(v22 + a2);
v23 = *(_QWORD *)&v73[8 * --v83 + 8];
v24 = bytesToInt(&v69);
setLocal(*(_QWORD *)(8LL * v81 + v76), v24, v23);
break;
case 12:
v25 = v84++;
v69 = *(_BYTE *)(v25 + a2);
v26 = v84++;
v70 = *(_BYTE *)(v26 + a2);
v27 = v84++;
v71 = *(_BYTE *)(v27 + a2);
v28 = v84++;
v72 = *(_BYTE *)(v28 + a2);
v29 = bytesToInt(&v69);
v30 = *(_QWORD *)(8LL * v81 + v76);
v31 = v83++;
*(_QWORD *)&v73[8 * v31 + 8] = getLocal(v30, v29);
break;
case 14:
decrementFunction(*(_QWORD *)(8LL * v81 + v76));
v84 = **((_DWORD **)ptr + --v82);
v81 = *(_DWORD *)(*((_QWORD *)ptr + v82) + 4LL);
break;
case 15:
printf("%lld\n", *(_QWORD *)&v73[8 * --v83 + 8]);
break;
case 16:
--v83;
break;
case 19:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v5 = v83++;
*(_QWORD *)&v73[8 * v5 + 8] = *((_QWORD *)&v74 + 1) / (__int64)v74;
break;
case 21:
v32 = v84++;
v69 = *(_BYTE *)(v32 + a2);
v33 = v84++;
v70 = *(_BYTE *)(v33 + a2);
v34 = v84++;
v71 = *(_BYTE *)(v34 + a2);
v35 = v84++;
v72 = *(_BYTE *)(v35 + a2);
if ( v82 >= v80 )
{
v80 *= 2;
ptr = realloc(ptr, 8LL * v80);
for ( j = v82; j < v80; ++j )
{
v36 = (void **)((char *)ptr + 8 * j);
*v36 = malloc(8uLL);
}
}
*(_DWORD *)(*((_QWORD *)ptr + v82) + 4LL) = v81;
**((_DWORD **)ptr + v82++) = v84;
v81 = bytesToInt(&v69);
v84 = **(_DWORD **)(8LL * v81 + v76);
incrementFunction(*(_QWORD *)(8LL * v81 + v76));
break;
case 22:
__isoc99_scanf("%lld", &v68);
v37 = v83++;
*(_QWORD *)&v73[8 * v37 + 8] = v68;
break;
case 23:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
if ( *((_QWORD *)&v74 + 1) != (_QWORD)v74 )
{
do
{
v38 = v84++;
v75 = *(_BYTE *)(v38 + a2);
}
while ( v75 != 14 );
}
break;
case 24:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v7 = v83++;
*(_QWORD *)&v73[8 * v7 + 8] = v74 | *((_QWORD *)&v74 + 1);
break;
case 25:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v8 = v83++;
*(_QWORD *)&v73[8 * v8 + 8] = v74 & *((_QWORD *)&v74 + 1);
break;
case 26:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v9 = v83++;
*(_QWORD *)&v73[8 * v9 + 8] = v74 ^ *((_QWORD *)&v74 + 1);
break;
case 27:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
if ( *((_QWORD *)&v74 + 1) == (_QWORD)v74 )
{
do
{
v39 = v84++;
v75 = *(_BYTE *)(v39 + a2);
}
while ( v75 != 14 );
}
break;
case 28:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
if ( *((__int64 *)&v74 + 1) < (__int64)v74 )
{
do
{
v40 = v84++;
v75 = *(_BYTE *)(v40 + a2);
}
while ( v75 != 14 );
}
break;
case 29:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
if ( *((__int64 *)&v74 + 1) > (__int64)v74 )
{
do
{
v41 = v84++;
v75 = *(_BYTE *)(v41 + a2);
}
while ( v75 != 14 );
}
break;
case 30:
v42 = v84++;
v69 = *(_BYTE *)(v42 + a2);
v43 = v84++;
v70 = *(_BYTE *)(v43 + a2);
v44 = v84++;
v71 = *(_BYTE *)(v44 + a2);
v45 = v84++;
v72 = *(_BYTE *)(v45 + a2);
v84 = bytesToInt(&v69);
break;
case 31:
v84 = *(_QWORD *)&v73[8 * --v83 + 8];
break;
case 32:
v46 = v84++;
v69 = *(_BYTE *)(v46 + a2);
v47 = v84++;
v70 = *(_BYTE *)(v47 + a2);
v48 = v84++;
v71 = *(_BYTE *)(v48 + a2);
v49 = v84++;
v72 = *(_BYTE *)(v49 + a2);
v50 = (int)bytesToInt(&v69);
++*(_BYTE *)(v50 + a2);
break;
case 33:
v51 = v84++;
v69 = *(_BYTE *)(v51 + a2);
v52 = v84++;
v70 = *(_BYTE *)(v52 + a2);
v53 = v84++;
v71 = *(_BYTE *)(v53 + a2);
v54 = v84++;
v72 = *(_BYTE *)(v54 + a2);
v55 = (int)bytesToInt(&v69);
--*(_BYTE *)(v55 + a2);
break;
case 34:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
*(_BYTE *)(v74 + a2) = *(_BYTE *)(*((_QWORD *)&v74 + 1) + a2);
break;
case 35:
v56 = v84++;
v69 = *(_BYTE *)(v56 + a2);
v57 = v84++;
v70 = *(_BYTE *)(v57 + a2);
v58 = v84++;
v71 = *(_BYTE *)(v58 + a2);
v59 = v84++;
v72 = *(_BYTE *)(v59 + a2);
v60 = *(unsigned __int8 *)((int)bytesToInt(&v69) + a2) << 8;
v61 = v60 + *(unsigned __int8 *)((int)bytesToInt(&v69) + 1LL + a2);
v62 = v83++;
*(_QWORD *)&v73[8 * v62 + 8] = v61;
break;
case 36:
v63 = v84++;
v69 = *(_BYTE *)(v63 + a2);
v64 = v84++;
v70 = *(_BYTE *)(v64 + a2);
v65 = v84++;
v71 = *(_BYTE *)(v65 + a2);
v66 = v84++;
v72 = *(_BYTE *)(v66 + a2);
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
if ( *((_QWORD *)&v74 + 1) == (_QWORD)v74 )
v84 = bytesToInt(&v69);
break;
default:
continue;
}
}
}
This function is definitly the meat of our program, this is the interpreter that runs the recurso bytecode and executes our program. The first thing I wanted to look into was checking what intializeFunctions does:
void *__fastcall initializeFunctions(char *a1)
{
v25 = 2;
v24 = 1;
ptr = malloc(0x10uLL);
v22 = 1;
for ( i = *a1; i == 17; i = a1[v16] )
{
v1 = v22++;
for ( i = a1[v1]; i != 18; i = a1[v2] )
v2 = v22++;
v3 = v22++;
v19[0] = a1[v3];
v4 = v22++;
v19[1] = a1[v4];
v5 = v22++;
v19[2] = a1[v5];
v6 = v22++;
v19[3] = a1[v6];
v7 = v22++;
v18[0] = a1[v7];
v8 = v22++;
v18[1] = a1[v8];
v9 = v22++;
v18[2] = a1[v9];
v10 = v22++;
v18[3] = a1[v10];
if ( v24 >= v25 )
{
v25 *= 2;
ptr = realloc(ptr, 8LL * v25);
}
v11 = (void **)((char *)ptr + 8 * v24 - 8);
*v11 = malloc(0x20uLL);
v12 = (_DWORD *)*((_QWORD *)ptr + v24 - 1);
*v12 = bytesToInt(v18);
*(_DWORD *)(*((_QWORD *)ptr + v24 - 1) + 20LL) = 0;
v13 = *((_QWORD *)ptr + v24 - 1);
*(_DWORD *)(v13 + 16) = bytesToInt(v19);
*(_DWORD *)(*((_QWORD *)ptr + v24 - 1) + 24LL) = v24 == 1;
v14 = *((_QWORD *)ptr + v24 - 1);
*(_QWORD *)(v14 + 8) = malloc(8LL * *(int *)(v14 + 16));
for ( j = 0; j < *(_DWORD *)(*((_QWORD *)ptr + v24 - 1) + 16LL); ++j )
{
v15 = (void **)(*(_QWORD *)(*((_QWORD *)ptr + v24 - 1) + 8LL) + 8LL * j);
*v15 = malloc(0xA0uLL);
}
++v24;
v16 = v22++;
}
return ptr;
}
This function starts by mallocing some space and then it checks if the first value in a1 is equal to 17. We then loop and increment a1 while a1 is not equal to 18. After that we fill in two different byte arrays of size 4 each while incrementing a1. We then check if one size is greater than another, if it is then we reallocate the memory we malloced at the start.
Then we malloc an object and fill in several of its parts, calling bytesToInt on the two arrays we populated earlier. After that we malloc some more space and then increment a1 yet again.
Back in the runProgram function we now want to look at the next section:
ptr = malloc(0x200uLL);
for ( i = 0; i < v80; ++i )
*((_QWORD *)ptr + i) = malloc(8uLL);
We malloc an original pointer and then look through and malloc again which means that this is probably a pointer to an array of pointers.
We now move on to the first part of the while loop:
while ( 1 )
{
result = *(unsigned __int8 *)(v84 + a2);
if ( (_BYTE)result == 13 )
return result;
v2 = v84++;
v75 = *(_BYTE *)(v2 + a2);
From the looks of this program and the fact that a2 is the program data we can assume that v84 is most likely the instruction pointer. We get the current opcode at the ip and if it is 13 then we return that value, if not, we increment the ip and set it to v2, then get the next opcode and save it to v75.
We now see a massive switch statement that switches on v75:
switch ( v75 )
{
case 1:
*((_QWORD *)&v74 + 1) = *(_QWORD *)&v73[8 * --v83 + 8];
*(_QWORD *)&v74 = *(_QWORD *)&v73[8 * --v83 + 8];
v3 = v83++;
*(_QWORD *)&v73[8 * v3 + 8] = *((_QWORD *)&v74 + 1) - v74;
break;
Since we know that v75 is the current opcode we can assume that the switch is for various different operations, so lets start with the first one since it looks fairly simple. We see that we decrement a value (v83) twice and use that to index from the array v73. We then increment v83 and then subtract the two values from each other and put them back into the array.
From this we can assume several things, the first is that this case is some sort of subtraction operation, the next thing we can assume is that v73 is the stack since we seem to be popping values off the stack and then pushing values back onto the stack. And the last thing we can assume is that v83 is the stack pointer. With this information we can start deciphering what the rest of the opcodes do.
For the sake of brevity I am not going to go super in depth into how to figure out each one since you know you have the return stack and the actual stack so the rest can be figured out quite easily, however, I will add a quick summary:
ISUB '\x01'
IMUL '\x02'
IADD '\x03'
FADD '\x04'
FSUB '\x05'
ITOF '\x06'
CCONST '\x07'
ICONST '\x08'
SCONST '\x09'
FCONST '\x0A'
STORE '\x0B'
LOAD '\x0C'
HALT '\x0D'
RET '\x0E'
PRINT '\x0F'
POP '\x10'
FUNC '\x11'
FUNCINFO '\x12'
IDIV '\x13'
FDIV '\x14'
CALL '\x15'
INPUT '\x16'
CMP '\x17'
OR '\x18'
AND '\x19'
XOR '\x1A'
NCMP '\x1B'
LTCMP '\x1C'
GTCMP '\x1D'
JMP '\x1E'
IJMP '\x1F'
INC '\x20'
DEC '\x21'
MOV '\x22'
MEMP '\x23'
CMPJMP '\x24'
ISUB
Pops the stack twice, subtracts 2nd item on stack from the 1st item on the stack, then pushes them back onto the stack.
IMUL
Pops the stack twice, multiplies the two values and pushes them onto the stack.
IDIV
Pops the stack twice, divides 2nd item on stack from the 1st item on the stack, then pushes them back onto the stack.
IADD
Pops the stack twice, adds the two values and pushes them onto the stack.
OR
Pops the stack twice, ors the two values and pushes them onto the stack.
AND
Pops the stack twice, ands the two values and pushes them onto the stack.
XOR
Pops the stack twice, xors the two values and pushes them onto the stack.
ICONST
Reads the next 8 bytes, converts them to long long, and pushes them onto the stack.
STORE
Reads the next 4 bytes, converts them to an int, then pops the stack and stores the value from the stack into the corresponding local variable.
LOAD
Reads the next 4 bytes, converts them to an int, then uses that int to get the local variable and pushes to the stack.
RET
Calls decrement function to get the last function on the call stack, sets ip to the address and current function to the function.
PRINT
Pops the stack and prints out the number.
POP
Pops a value from the stack and discards it.
CALL
Reads the next 4 bytes, converts to an int, and uses that to call the selected function. Sets the address of the return stack to the current ip.
INPUT
Reads in a long long and pushes to the stack.
CMP
Pops 2 values off the stack, and checks if they are equal to each other, if they are not equal then continue till we find a return.
NCMP
Pops 2 values off the stack, and checks if they are equal to each other, if they are equal then continue till we find a return.
LTCMP
Pops 2 values off the stack, and compares them to each other, if the 1st value is less than the 2nd value then continue till we find a return.
GTCMP
Pops 2 values off the stack, and compares them to each other, if the 1st value is greater than the 2nd value then continue till we find a return.
HALT
Ends the program.
JMP
Reads next 4 bytes, converts to int, and sets ip to that value.
IJMP
Pops stack and sets ip to that value.
INC
Reads next 4 bytes, converts to int, and increases the value of the opcode at that location.
DEC
Reads next 4 bytes, converts to int, and decreases the value of the opcode at that location.
MOV
Pops 2 values from the stack and sets the opcode at the 2nd location with the one from the 1st location.
MEMP
Reads next 4 bytes, then reads the opcode at that location and the next opcode, converts them to an integer, and pushes to the stack.
CMPJMP
Reads next 4 bytes, pops 2 values from the stack, if they are equal then set ip to the value of the number we read.
From this point I needed to make a disassembler, I decided to make a plugin for Binary Ninja to do this step since that way I would be able to have a nice view.
I first made a disassembly file named recwrapper.py
:
from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken
from binaryninja.enums import InstructionTextTokenType, BranchType
from binaryninjaui import UIContext
ISUB = int('01', 16)
IMUL = int('02', 16)
IADD = int('03', 16)
FADD = int('04', 16)
FSUB = int('05', 16)
ITOF = int('06', 16)
CCONST = int('07', 16)
ICONST = int('08', 16)
SCONST = int('09', 16)
FCONST = int('0A', 16)
STORE = int('0B', 16)
LOAD = int('0C', 16)
HALT = int('0D', 16)
RET = int('0E', 16)
PRINT = int('0F', 16)
POP = int('10', 16)
FUNC = int('11', 16)
FUNCINFO = int('12', 16)
IDIV = int('13', 16)
FDIV = int('14', 16)
CALL = int('15', 16)
INPUT = int('16', 16)
CMP = int('17', 16)
OR = int('18', 16)
AND = int('19', 16)
XOR = int('1A', 16)
NCMP = int('1B', 16)
LTCMP = int('1C', 16)
GTCMP = int('1D', 16)
JMP = int('1E', 16)
IJMP = int('1F', 16)
INC = int('20', 16)
DEC = int('21', 16)
MOV = int('22', 16)
MEMP = int('23', 16)
CMPJMP = int('24', 16)
def bytesToInt(data, addr):
val = 0
for i in range(4):
val += (data[addr + i] << (24 - (8 * i)))
return val
def bytesToLongLong(data, addr):
val = 0
for i in range(8):
val += (data[addr + i] << (56 - (8 * i)))
return val
class Disassembler():
def __init__(self):
self.bv = None
def getBV(self):
if self.bv == None:
ac = UIContext.activeContext()
if ac != None:
cv = ac.getCurrentViewFrame()
if cv != None:
self.bv = cv.getCurrentBinaryView()
return True
return False
return False
return True
def disasm(self, data, addr):
while not self.getBV():
pass
op = data[0]
if op == ISUB:
return ([ "ISUB"], 1)
elif op == IMUL:
return ([ "IMUL"], 1)
elif op == IDIV:
return ([ "IDIV"], 1)
elif op == IADD:
return ([ "IADD"], 1)
elif op == ICONST:
return ([ "ICONST", " ", str(bytesToLongLong(data, 1))], 9)
elif op == STORE:
return ([ "STORE", " ", str(bytesToInt(data, 1))], 5)
elif op == LOAD:
return ([ "LOAD", " ", str(bytesToInt(data, 1))], 5)
elif op == HALT:
return ([ "HALT"], 1)
elif op == RET:
return ([ "RET"], 1)
elif op == PRINT:
return ([ "PRINT"], 1)
elif op == POP:
return ([ "POP"], 1)
elif op == CALL:
return ([ "CALL", " ", str(bytesToInt(data, 1))], 5)
elif op == INPUT:
return ([ "INPUT"], 1)
elif op == CMP:
return ([ "CMP"], 1)
elif op == NCMP:
return ([ "NCMP"], 1)
elif op == LTCMP:
return ([ "LTCMP"], 1)
elif op == GTCMP:
return ([ "GTCMP"], 1)
elif op == OR:
return ([ "OR"], 1)
elif op == AND:
return ([ "AND"], 1)
elif op == XOR:
return ([ "XOR"], 1)
elif op == JMP:
return ([ "JMP", " ", str(bytesToInt(data, 1))], 5)
elif op == IJMP:
return ([ "IJMP"], 1)
elif op == INC:
return ([ "INC", " ", str(bytesToInt(data, 1))], 5)
elif op == DEC:
return ([ "DEC", " ", str(bytesToInt(data, 1))], 5)
elif op == MOV:
return ([ "MOV"], 1)
elif op == MEMP:
return ([ "MEMP", " ", str(bytesToInt(data, 1))], 5)
elif op == CMPJMP:
return ([ "CMPJMP", " ", str(bytesToInt(data, 1))], 5)
return ([ "empty"], 1)
What this code does is it takes in a few bytes and figures out what instructions they are supposed to be. I then tied this into a __init__.py
which contained a custom view so that I could see the code:
from binaryninja.architecture import Architecture
from binaryninja.binaryview import *
from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken
from binaryninja.enums import InstructionTextTokenType, BranchType, SegmentFlag, SymbolType
from binaryninja.types import *
import recwrapper
from binaryninjaui import UIContext
use_default_loader_settings = True
disasm = recwrapper.Disassembler()
class Recurso(Architecture):
name = 'Recurso'
address_size = 4
default_int_size = 8
max_instr_length = 23
stack_pointer = RegisterInfo("SP", 4)
def get_instruction_info(self, data, addr):
(instrTxt, instrLen) = disasm.disasm(data, addr)
if instrLen == 0:
return None
result = InstructionInfo()
print()
for txt in instrTxt:
if txt == "CMP" or txt == "NCMP" or txt == "GTCMP" or txt == "LTCMP":
endLen = instrLen
result.add_branch(BranchType.TrueBranch, endLen + addr)
(instructs, length) = disasm.disasm(disasm.bv.read(endLen + addr, 23), addr)
while (instructs[0] != "RET"):
endLen += length
(instructs, length) = disasm.disasm(disasm.bv.read(endLen + addr, 23), addr)
result.add_branch(BranchType.FalseBranch, endLen + addr + length)
elif instrTxt[0] == "CALL" and txt.isdigit():
result.add_branch(BranchType.CallDestination, disasm.bv.functions[int(txt)].start)
elif txt == "RET" or txt == "HALT":
result.add_branch(BranchType.FunctionReturn)
result.length = instrLen
return result
def get_instruction_text(self, data, addr):
(instrTxt, instrLen) = disasm.disasm(data, addr)
result = []
for txt in instrTxt:
if txt.isdigit() and (instrTxt[0] == "STORE" or instrTxt[0] == "LOAD"):
result.append(InstructionTextToken(InstructionTextTokenType.RegisterToken, txt))
elif txt.isdigit() and instrTxt[0] == "CALL" :
result.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, disasm.bv.functions[int(txt)].name, disasm.bv.functions[int(txt)].start))
elif txt.isdigit() and (instrTxt[0] == "JMP" or instrTxt[0] == "INC" or instrTxt[0] == "DEC"):
result.append(InstructionTextToken(InstructionTextTokenType.IntegerToken, hex(int(txt) + disasm.bv.functions[0].start), int(txt) + disasm.bv.functions[0].start))
elif txt.isdigit():
result.append(InstructionTextToken(InstructionTextTokenType.IntegerToken, txt, int(txt)))
else:
result.append(InstructionTextToken(InstructionTextTokenType.TextToken, txt))
return result, instrLen
def get_instruction_low_level_il(self, data, addr, il):
return None
# Define our view
class RecursoView(BinaryView):
name = 'RecursoView'
long_name = 'Recurso View'
# check if this is an MSDOS file
@classmethod
def is_valid_for_data(cls,data):
if data.file.original_filename.endswith(".recc"):
return True
return False
# intialize the binary view
def __init__(self, data):
BinaryView.__init__(self, parent_view = data, file_metadata = data.file)
self.platform = Architecture['Recurso'].standalone_platform
self.data = data
def defineFunctions(self, data, start, end):
while start < end:
op = data[start]
curName = ""
if op == b'\x11':
start += 1
op = data[start]
while op != b'\x12':
curName += op.decode()
start += 1
op = data[start]
start += 1
localsCount = int.from_bytes(data.read(start, 4), "big")
start += 4
startAddress = int.from_bytes(data.read(start, 4), "big")
start += 4
self.add_function(startAddress + end)
self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol,startAddress + end, curName))
# initialize our view
def init(self):
start = int.from_bytes(self.data.read(0, 4), "little") + 4
self.add_auto_segment(4, start - 1, 4, start - 1, SegmentFlag.SegmentReadable|SegmentFlag.SegmentContainsData)
self.add_auto_segment(start, len(self.data) - start, start, len(self.data) - start, SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable|SegmentFlag.SegmentContainsCode)
self.add_entry_point(start)
self.defineFunctions(self.data, 4, start)
return True
def perform_is_executable(self):
return True
Recurso.register()
RecursoView.register()
The view basically parsed out what components of the data were function data and what was opcodes, remember that the first 4 bytes tell us the length of the function data and then rest is opcodes. It then calls the diassembler and creates branches so that we can get a nicer view of the program. When we run the disassembler we get:
0000018e int32_t main()
0000018e 0c00000000 LOAD 0
00000193 16 INPUT
00000194 0b00000000 STORE 0
00000199 0c00000000 LOAD 0
0000019e 1500000013 CALL check
000001a3 0b00000001 STORE 1
000001a8 0c00000001 LOAD 1
000001ad 0f PRINT
000001ae 0d HALT
000001af int32_t antidebug()
000001af 0b00000000 STORE 0
000001b4 0c00000000 LOAD 0
000001b9 0800000000000000…ICONST 0
000001c2 17 CMP
000001c3 0800000000000000…ICONST 0
000001cc 0e RET
000001cd 0800000000000000…ICONST 1
000001d6 0c00000000 LOAD 0
000001db 01 ISUB
000001dc 0b00000001 STORE 1
000001e1 0800000000000000…ICONST 1
000001ea 0c00000000 LOAD 0
000001ef 03 IADD
000001f0 0b00000000 STORE 0
000001f5 0800000000000000…ICONST 1
000001fe 0c00000000 LOAD 0
00000203 01 ISUB
00000204 0b00000000 STORE 0
00000209 0800000000000000…ICONST 1
00000212 0c00000000 LOAD 0
00000217 02 IMUL
00000218 0b00000000 STORE 0
0000021d 0800000000000000…ICONST 1
00000226 0c00000000 LOAD 0
0000022b 13 IDIV
0000022c 0b00000000 STORE 0
00000231 0800000000000000…ICONST 1
0000023a 0c00000000 LOAD 0
0000023f 19 AND
00000240 0b00000000 STORE 0
00000245 0800000000000000…ICONST 1
0000024e 0c00000000 LOAD 0
00000253 18 OR
00000254 0b00000000 STORE 0
00000259 0800000000000000…ICONST 0
00000262 0c00000000 LOAD 0
00000267 1a XOR
00000268 0b00000000 STORE 0
0000026d 0c00000001 LOAD 1
00000272 1500000001 CALL antidebug
00000277 0800000000000000…ICONST 0
00000280 0e RET
00000281 int32_t itsATwap()
00000281 0b00000000 STORE 0
00000286 0c00000000 LOAD 0
0000028b 080000000000000e…ICONST 3713
00000294 17 CMP
00000295 0800000000000000…ICONST 1
0000029e 0e RET
0000029f 0c00000001 LOAD 1
000002a4 16 INPUT
000002a5 0b00000001 STORE 1
000002aa 0c00000001 LOAD 1
000002af 1500000001 CALL antidebug
000002b4 0c00000000 LOAD 0
000002b9 1500000002 CALL itsATwap
000002be 0800000000000000…ICONST 0
000002c7 0e RET
000002c8 int32_t oddCheck()
000002c8 0b00000003 STORE 3
000002cd 0b00000002 STORE 2
000002d2 0b00000001 STORE 1
000002d7 0b00000000 STORE 0
000002dc 0c00000000 LOAD 0
000002e1 0800000000000075…ICONST 30001
000002ea 17 CMP
000002eb 0c00000001 LOAD 1
000002f0 0800000000000067…ICONST 26419
000002f9 17 CMP
000002fa 0c00000002 LOAD 2
000002ff 08000038645f3731…ICONST 62003745337707
00000308 17 CMP
00000309 0c00000003 LOAD 3
0000030e 080000000000006d…ICONST 27955
00000317 17 CMP
00000318 0800000000000000…ICONST 1
00000321 0e RET
00000322 0800000000000000…ICONST 0
0000032b 0e RET
0000032c int32_t evenCheck()
0000032c 0b00000003 STORE 3
00000331 0b00000002 STORE 2
00000336 0b00000001 STORE 1
0000033b 0b00000000 STORE 0
00000340 0c00000000 LOAD 0
00000345 0800000000000064…ICONST 25695
0000034e 1b NCMP
0000034f 0c00000001 LOAD 1
00000354 0800000000375f67…ICONST 928999216
0000035d 1b NCMP
0000035e 0c00000002 LOAD 2
00000363 0800000000000033…ICONST 13151
0000036c 1b NCMP
0000036d 0c00000003 LOAD 3
00000372 0800000000000000…ICONST 125
0000037b 1b NCMP
0000037c 0800000000000000…ICONST 0
00000385 0e RET
00000386 0800000000000000…ICONST 1
0000038f 0e RET
00000390 int32_t finalCheck()
00000390 0c00000000 LOAD 0
00000395 0c00000001 LOAD 1
0000039a 0c00000002 LOAD 2
0000039f 0c00000003 LOAD 3
000003a4 0c00000004 LOAD 4
000003a9 0c00000005 LOAD 5
000003ae 0c00000006 LOAD 6
000003b3 0c00000007 LOAD 7
000003b8 16 INPUT
000003b9 0b00000000 STORE 0
000003be 16 INPUT
000003bf 0b00000001 STORE 1
000003c4 16 INPUT
000003c5 0b00000002 STORE 2
000003ca 16 INPUT
000003cb 0b00000003 STORE 3
000003d0 16 INPUT
000003d1 0b00000004 STORE 4
000003d6 16 INPUT
000003d7 0b00000005 STORE 5
000003dc 16 INPUT
000003dd 0b00000006 STORE 6
000003e2 16 INPUT
000003e3 0b00000007 STORE 7
000003e8 0c00000000 LOAD 0
000003ed 0c00000002 LOAD 2
000003f2 0c00000004 LOAD 4
000003f7 0c00000006 LOAD 6
000003fc 1500000003 CALL oddCheck
00000401 0b00000008 STORE 8
00000406 0c00000001 LOAD 1
0000040b 0c00000003 LOAD 3
00000410 0c00000005 LOAD 5
00000415 0c00000007 LOAD 7
0000041a 1500000004 CALL evenCheck
0000041f 0b00000009 STORE 9
00000424 0c00000009 LOAD 9
00000429 0c00000008 LOAD 8
0000042e 19 AND
0000042f 0b0000000a STORE 10
00000434 0c0000000a LOAD 10
00000439 0e RET
0000043a int32_t checkThemAll()
0000043a 0b00000005 STORE 5
0000043f 0b00000004 STORE 4
00000444 0b00000003 STORE 3
00000449 0b00000002 STORE 2
0000044e 0b00000001 STORE 1
00000453 0b00000000 STORE 0
00000458 0c00000000 LOAD 0
0000045d 08ffffffd42fb64c…ICONST 18446743885531466769
00000466 17 CMP
00000467 0c00000001 LOAD 1
0000046c 08000009a2991485…ICONST 10593957610752
00000475 17 CMP
00000476 0c00000002 LOAD 2
0000047b 080000001ba4e9bf…ICONST 118730899270
00000484 17 CMP
00000485 0c00000003 LOAD 3
0000048a 0800000139813d75…ICONST 1346493052268
00000493 17 CMP
00000494 0c00000004 LOAD 4
00000499 080000005f755f73…ICONST 409991082872
000004a2 17 CMP
000004a3 0c00000005 LOAD 5
000004a8 0800000018002c00…ICONST 103082098739
000004b1 17 CMP
000004b2 0800000000000000…ICONST 1
000004bb 0e RET
000004bc 0800000000000000…ICONST 0
000004c5 0e RET
000004c6 int32_t nextNextNextCheck()
000004c6 0c00000000 LOAD 0
000004cb 0c00000001 LOAD 1
000004d0 0c00000002 LOAD 2
000004d5 0c00000003 LOAD 3
000004da 0c00000004 LOAD 4
000004df 16 INPUT
000004e0 0b00000000 STORE 0
000004e5 16 INPUT
000004e6 0b00000001 STORE 1
000004eb 16 INPUT
000004ec 0b00000002 STORE 2
000004f1 16 INPUT
000004f2 0b00000003 STORE 3
000004f7 16 INPUT
000004f8 0b00000004 STORE 4
000004fd 0c00000001 LOAD 1
00000502 0c00000000 LOAD 0
00000507 01 ISUB
00000508 0b00000005 STORE 5
0000050d 0c00000004 LOAD 4
00000512 0c00000000 LOAD 0
00000517 02 IMUL
00000518 0b00000006 STORE 6
0000051d 0c00000002 LOAD 2
00000522 0c00000005 LOAD 5
00000527 03 IADD
00000528 0b00000007 STORE 7
0000052d 0c00000003 LOAD 3
00000532 0c00000002 LOAD 2
00000537 03 IADD
00000538 0c00000001 LOAD 1
0000053d 03 IADD
0000053e 0c00000000 LOAD 0
00000543 03 IADD
00000544 0b00000008 STORE 8
00000549 0c00000004 LOAD 4
0000054e 0c00000003 LOAD 3
00000553 18 OR
00000554 0b00000009 STORE 9
00000559 0c00000002 LOAD 2
0000055e 0c00000003 LOAD 3
00000563 01 ISUB
00000564 0b0000000a STORE 10
00000569 0c00000005 LOAD 5
0000056e 0c00000006 LOAD 6
00000573 0c00000007 LOAD 7
00000578 0c00000008 LOAD 8
0000057d 0c00000009 LOAD 9
00000582 0c0000000a LOAD 10
00000587 1500000006 CALL checkThemAll
0000058c 0b0000000b STORE 11
00000591 1500000005 CALL finalCheck
00000596 0b0000000c STORE 12
0000059b 0c0000000c LOAD 12
000005a0 0c0000000b LOAD 11
000005a5 19 AND
000005a6 0b0000000d STORE 13
000005ab 0c0000000d LOAD 13
000005b0 0e RET
000005b1 int32_t checkity()
000005b1 0b00000004 STORE 4
000005b6 0b00000003 STORE 3
000005bb 0b00000002 STORE 2
000005c0 0b00000001 STORE 1
000005c5 0b00000000 STORE 0
000005ca 0c00000000 LOAD 0
000005cf 0800000071e2fcfb…ICONST 489139534831
000005d8 17 CMP
000005d9 0c00000001 LOAD 1
000005de 0800000000000000…ICONST 1
000005e7 17 CMP
000005e8 0c00000002 LOAD 2
000005ed 0800000000000000…ICONST 2
000005f6 17 CMP
000005f7 0c00000003 LOAD 3
000005fc 0800000000000000…ICONST 3
00000605 17 CMP
00000606 0c00000004 LOAD 4
0000060b 0800000000000000…ICONST 4
00000614 17 CMP
00000615 0800000000000000…ICONST 1
0000061e 0e RET
0000061f 080000000000001c…ICONST 7331
00000628 1500000001 CALL antidebug
0000062d 0800000000000000…ICONST 0
00000636 0e RET
00000637 int32_t midThree()
00000637 0b00000000 STORE 0
0000063c 0c00000000 LOAD 0
00000641 080000000035305f…ICONST 892362496
0000064a 1b NCMP
0000064b 0800000000000000…ICONST 0
00000654 0e RET
00000655 0800000000000000…ICONST 1
0000065e 0e RET
0000065f int32_t topAndBottom()
0000065f 0b00000000 STORE 0
00000664 0c00000000 LOAD 0
00000669 0800000000000000…ICONST 7
00000672 17 CMP
00000673 0800000000000000…ICONST 1
0000067c 0e RET
0000067d 0800000000000000…ICONST 0
00000686 0e RET
00000687 int32_t checkTheBigs()
00000687 0b00000001 STORE 1
0000068c 0b00000000 STORE 0
00000691 0c00000000 LOAD 0
00000696 0c00000001 LOAD 1
0000069b 1d GTCMP
0000069c 0c00000001 LOAD 1
000006a1 0c00000001 LOAD 1
000006a6 1b NCMP
000006a7 0800000000000000…ICONST 0
000006b0 0e RET
000006b1 0c00000000 LOAD 0
000006b6 0c00000001 LOAD 1
000006bb 01 ISUB
000006bc 0b00000002 STORE 2
000006c1 0c00000002 LOAD 2
000006c6 0800000000000000…ICONST 1
000006cf 0800000000000000…ICONST 2
000006d8 0800000000000000…ICONST 3
000006e1 0800000000000000…ICONST 4
000006ea 1500000008 CALL checkity
000006ef 0b00000003 STORE 3
000006f4 0800000000ffffff…ICONST 4294967040
000006fd 0c00000001 LOAD 1
00000702 19 AND
00000703 0b00000004 STORE 4
00000708 0c00000004 LOAD 4
0000070d 1500000009 CALL midThree
00000712 0b00000005 STORE 5
00000717 0800000000000000…ICONST 255
00000720 0c00000000 LOAD 0
00000725 19 AND
00000726 0b00000006 STORE 6
0000072b 08000000ff000000…ICONST 1095216660480
00000734 0c00000001 LOAD 1
00000739 19 AND
0000073a 0b00000007 STORE 7
0000073f 0c00000007 LOAD 7
00000744 0c00000006 LOAD 6
00000749 1a XOR
0000074a 0b00000008 STORE 8
0000074f 0800000000000000…ICONST 1
00000758 0b00000009 STORE 9
0000075d 1500000007 CALL nextNextNextCheck
00000762 0b0000000a STORE 10
00000767 0c0000000a LOAD 10
0000076c 0c00000009 LOAD 9
00000771 19 AND
00000772 0c00000005 LOAD 5
00000777 19 AND
00000778 0c00000003 LOAD 3
0000077d 19 AND
0000077e 0b0000000b STORE 11
00000783 0c0000000b LOAD 11
00000788 0e RET
00000789 int32_t isItOr()
00000789 0b00000000 STORE 0
0000078e 0c00000000 LOAD 0
00000793 0800000000000000…ICONST 99
0000079c 17 CMP
0000079d 0800000000000000…ICONST 1
000007a6 0e RET
000007a7 0800000000000000…ICONST 0
000007b0 0e RET
000007b1 int32_t isItXor()
000007b1 0b00000000 STORE 0
000007b6 0c00000000 LOAD 0
000007bb 0800000000000000…ICONST 28
000007c4 1b NCMP
000007c5 0800000000000000…ICONST 0
000007ce 0e RET
000007cf 0800000000000000…ICONST 1
000007d8 0e RET
000007d9 int32_t keepGoing()
000007d9 080000000000001c…ICONST 7331
000007e2 1500000001 CALL antidebug
000007e7 0c00000000 LOAD 0
000007ec 0c00000001 LOAD 1
000007f1 16 INPUT
000007f2 0b00000000 STORE 0
000007f7 16 INPUT
000007f8 0b00000001 STORE 1
000007fd 0c00000001 LOAD 1
00000802 0c00000000 LOAD 0
00000807 1a XOR
00000808 0b00000002 STORE 2
0000080d 0c00000002 LOAD 2
00000812 150000000d CALL isItXor
00000817 0b00000003 STORE 3
0000081c 0c00000001 LOAD 1
00000821 0c00000000 LOAD 0
00000826 19 AND
00000827 0b00000002 STORE 2
0000082c 0c00000002 LOAD 2
00000831 150000000c CALL isItOr
00000836 0b00000004 STORE 4
0000083b 0c00000005 LOAD 5
00000840 16 INPUT
00000841 0b00000005 STORE 5
00000846 0c00000005 LOAD 5
0000084b 1500000002 CALL itsATwap
00000850 0c00000006 LOAD 6
00000855 0c00000007 LOAD 7
0000085a 16 INPUT
0000085b 0b00000006 STORE 6
00000860 16 INPUT
00000861 0b00000007 STORE 7
00000866 0c00000006 LOAD 6
0000086b 0c00000007 LOAD 7
00000870 150000000b CALL checkTheBigs
00000875 0b00000008 STORE 8
0000087a 0c00000008 LOAD 8
0000087f 0c00000004 LOAD 4
00000884 19 AND
00000885 0c00000003 LOAD 3
0000088a 19 AND
0000088b 0b00000009 STORE 9
00000890 0c00000009 LOAD 9
00000895 0e RET
00000896 int32_t wowzaACheck2()
00000896 0b00000000 STORE 0
0000089b 0c00000000 LOAD 0
000008a0 0800000000000069…ICONST 26960
000008a9 1d GTCMP
000008aa 0800000000000000…ICONST 1
000008b3 0e RET
000008b4 0800000000000000…ICONST 0
000008bd 0e RET
000008be int32_t wowzaACheck()
000008be 0b00000000 STORE 0
000008c3 0c00000000 LOAD 0
000008c8 080000000000041d…ICONST 269700
000008d1 1c LTCMP
000008d2 0800000000000000…ICONST 1
000008db 0e RET
000008dc 0800000000000000…ICONST 0
000008e5 0e RET
000008e6 int32_t yetAnotherCheck()
000008e6 0b00000000 STORE 0
000008eb 0c00000000 LOAD 0
000008f0 080000000000006d…ICONST 28025
000008f9 1b NCMP
000008fa 0800000000000000…ICONST 0
00000903 0e RET
00000904 0800000000000000…ICONST 1
0000090d 0e RET
0000090e int32_t anotherCheck()
0000090e 0b00000000 STORE 0
00000913 0c00000000 LOAD 0
00000918 0800000000000004…ICONST 1057
00000921 17 CMP
00000922 0800000000000000…ICONST 1
0000092b 0e RET
0000092c 0800000000000000…ICONST 0
00000935 0e RET
00000936 int32_t check()
00000936 0b00000000 STORE 0
0000093b 0c00000000 LOAD 0
00000940 0800000000000000…ICONST 102
00000949 1b NCMP
0000094a 0800000000000000…ICONST 0
00000953 0e RET
00000954 0c00000000 LOAD 0
00000959 16 INPUT
0000095a 0b00000000 STORE 0
0000095f 0800000000000005…ICONST 1337
00000968 0c00000000 LOAD 0
0000096d 19 AND
0000096e 0b00000002 STORE 2
00000973 0c00000002 LOAD 2
00000978 1500000012 CALL anotherCheck
0000097d 0b00000003 STORE 3
00000982 0800000000000005…ICONST 1337
0000098b 0c00000000 LOAD 0
00000990 18 OR
00000991 0b00000004 STORE 4
00000996 0c00000004 LOAD 4
0000099b 1500000011 CALL yetAnotherCheck
000009a0 0b00000005 STORE 5
000009a5 0800000000000005…ICONST 1337
000009ae 0c00000000 LOAD 0
000009b3 1a XOR
000009b4 0b00000006 STORE 6
000009b9 0c00000006 LOAD 6
000009be 1500000010 CALL wowzaACheck
000009c3 0b00000007 STORE 7
000009c8 0c00000006 LOAD 6
000009cd 1500000010 CALL wowzaACheck
000009d2 0b00000008 STORE 8
000009d7 150000000e CALL keepGoing
000009dc 0b00000009 STORE 9
000009e1 0c00000009 LOAD 9
000009e6 0c00000008 LOAD 8
000009eb 19 AND
000009ec 0c00000007 LOAD 7
000009f1 19 AND
000009f2 0c00000005 LOAD 5
000009f7 19 AND
000009f8 0c00000003 LOAD 3
000009fd 19 AND
000009fe 0b0000000a STORE 10
00000a03 0c0000000a LOAD 10
00000a08 0e RET
Luckily Recurso bytecode translate pretty easily straight into its high level representation so from here we can simply turn it back into its source code:
int antidebug(int a)
| 0, 0 |
{
int b = a - 1;
a = a + 1;
a = a - 1;
a = a * 1;
a = a / 1;
a = a & 1;
a = a | 1;
a = a ^ 0;
antidebug(b);
return 0;
}
int itsATwap(int a)
| 3713, 1 |
{
int b;
input b;
antidebug(b);
itsATwap(a);
return 0;
}
int oddCheck(int a, int b, int c, int d)
| 30001, 26419, 62003745337707, 27955, 1 |
{
return 0;
}
int evenCheck(int a, int b, int c, int d)
| !25695, !928999216, !13151, !125, 0 |
{
return 1;
}
int finalCheck()
{
int a;
int b;
int c;
int d;
int e;
int f;
int g;
int h;
input a;
input b;
input c;
input d;
input e;
input f;
input g;
input h;
int resp = oddCheck(a,c,e,g);
int resp2 = evenCheck(b,d,f,h);
int res = resp & resp2;
return res;
}
int checkThemAll(int a, int b, int c, int d, int e, int f)
| -188178084847, 10593957610752, 118730899270, 1346493052268, 409991082872, 103082098739, 1 |
{
return 0;
}
int nextNextNextCheck()
{
int a;
int b;
int c;
int d;
int e;
input a;
input b;
input c;
input d;
input e;
int f = a - b;
int g = a * e;
int h = f + c;
int i = a + b + c + d;
int j = d | e;
int k = d - c;
int resp = checkThemAll(f,g,h,i,j,k);
int resp2 = finalCheck();
int res = resp & resp2;
return res;
}
int checkity(int a, int b, int c, int d, int e)
| 489139534831, 1, 2, 3, 4, 1 |
{
antidebug(7331);
return 0;
}
int midThree(int a)
| !892362496, 0 |
{
return 1;
}
int topAndBottom(int a)
| 7, 1 |
{
return 0;
}
int checkTheBigs(int a, int b)
| >b, !b, 0 |
{
int c = b - a;
int resp = checkity(c, 1, 2, 3, 4);
int d = b & 4294967040;
int resp2 = midThree(d);
int e = a & 255;
int f = b & 1095216660480;
int g = e ^ f;
int resp3 = 1;
int resp4 = nextNextNextCheck();
int res = resp & resp2 & resp3 & resp4;
return res;
}
int isItOr(int a)
| 99, 1 |
{
return 0;
}
int isItXor(int a)
| !28, 0 |
{
return 1;
}
int keepGoing()
{
antidebug(7331);
int a;
int b;
input a;
input b;
int c = a ^ b;
int resp = isItXor(c);
c = a & b;
int resp2 = isItOr(c);
int twap;
input twap;
itsATwap(twap);
int d;
int e;
input d;
input e;
int resp3 = checkTheBigs(d, e);
int res = resp & resp2 & resp3;
return res;
}
int wowzaACheck2(int a)
| >26960, 1 |
{
return 0;
}
int wowzaACheck(int a)
| <269700, 1 |
{
return 0;
}
int yetAnotherCheck(int a)
| !28025, 0 |
{
return 1;
}
int anotherCheck(int a)
| 1057, 1 |
{
return 0;
}
int check(int a)
| !102, 0 |
{
int a;
input a;
int b = a & 1337;
int resp = anotherCheck(b);
int c = a | 1337;
int resp2 = yetAnotherCheck(c);
int d = a ^ 1337;
int resp3 = wowzaACheck(d);
int resp4 = wowzaACheck(d);
int resp5 = keepGoing();
int res = resp & resp2 & resp3 & resp4 & resp5;
return res;
}
int a;
input a;
int b = check(a);
print(b);
From here we can see that we have a bunch of conditions which means that we can simply write a z3py script.
pure constriants (no guessing):
from z3 import *
maina = BitVec('maina', 64)
checka = BitVec('checka', 64)
keepgoinga = BitVec('keepgoinga', 64)
keepgoingb = BitVec('keepgoingb', 64)
checkthebigsa = BitVec('checkthebigsa', 40)
checkthebigsb = BitVec('checkthebigsb', 40)
nextNextNextChecka = BitVec('nextNextNextChecka', 64)
nextNextNextCheckb = BitVec('nextNextNextCheckb', 64)
nextNextNextCheckc = BitVec('nextNextNextCheckc', 64)
nextNextNextCheckd = BitVec('nextNextNextCheckd', 64)
nextNextNextChecke = BitVec('nextNextNextChecke', 64)
finalChecka = BitVec('finalChecka', 64)
finalCheckb = BitVec('finalCheckb', 64)
finalCheckc = BitVec('finalCheckc', 64)
finalCheckd = BitVec('finalCheckd', 64)
finalChecke = BitVec('finalChecke', 64)
finalCheckf = BitVec('finalCheckf', 64)
finalCheckg = BitVec('finalCheckg', 64)
finalCheckh = BitVec('finalCheckh', 64)
vals = [maina, checka, keepgoinga, keepgoingb, checkthebigsa, checkthebigsb, nextNextNextChecka, nextNextNextCheckb,
nextNextNextCheckc, nextNextNextCheckd, nextNextNextChecke, finalChecka, finalCheckb, finalCheckc,
finalCheckd, finalChecke, finalCheckf, finalCheckg, finalCheckh]
s = Solver()
s.add(maina == 102)
s.add(checka & 1337 == 1057)
s.add(checka | 1337 == 28025)
s.add(checka ^ 1337 < 269700)
s.add(keepgoinga ^ keepgoingb == 28)
s.add(keepgoinga & keepgoingb == 99)
s.add(checkthebigsb - checkthebigsa == 489139534831)
s.add(checkthebigsb & 4294967040 == 892362496)
s.add(checkthebigsb < checkthebigsa)
s.add(nextNextNextChecka - nextNextNextCheckb == -188178084847)
s.add(nextNextNextChecka * nextNextNextChecke == 10593957610752)
s.add((nextNextNextChecka - nextNextNextCheckb) + nextNextNextCheckc == 118730899270)
s.add(nextNextNextChecka + nextNextNextCheckb + nextNextNextCheckc + nextNextNextCheckd == 1346493052268)
s.add(nextNextNextCheckd | nextNextNextChecke == 409991082872)
s.add(nextNextNextCheckd - nextNextNextCheckc == 103082098739)
s.add(finalChecka == 30001)
s.add(finalCheckb == 25695)
s.add(finalCheckc == 26419)
s.add(finalCheckd == 928999216)
s.add(finalChecke == 62003745337707)
s.add(finalCheckf == 13151)
s.add(finalCheckg == 27955)
s.add(finalCheckh == 125)
s.check()
m = s.model()
print(m)
total_str = ""
for i in vals:
hex_str = hex(int(str(m[i])))[2:].replace("00", '')
cur_str = ""
for i in range(0, len(hex_str), 2):
try:
cur_str += bytes.fromhex(hex_str[i:i+2]).decode("ASCII")
except:
cur_str += "."
print(cur_str)
total_str += cur_str
print(total_str)
Which gives you:
f
la
c
`R3c%
.50_
3c0mp
_3z!_
Gu3s5
_u_sh
0
u1
d_
g3
7_g0
8d_71k
3_
m3
}
flac`R3c%.50_3c0mp_3z!_Gu3s5_u_sh0u1d_g37_g08d_71k3_m3}
R3c..50 makes it fairly obvious that it is Recurso in some form. and that we don't need that initial value in front so it is 4 bytes.
and you know it should be flag{ so you can add some more constraints:
from z3 import *
maina = BitVec('maina', 64)
checka = BitVec('checka', 64)
keepgoinga = BitVec('keepgoinga', 64)
keepgoingb = BitVec('keepgoingb', 64)
checkthebigsa = BitVec('checkthebigsa', 40)
checkthebigsb = BitVec('checkthebigsb', 40)
nextNextNextChecka = BitVec('nextNextNextChecka', 64)
nextNextNextCheckb = BitVec('nextNextNextCheckb', 64)
nextNextNextCheckc = BitVec('nextNextNextCheckc', 64)
nextNextNextCheckd = BitVec('nextNextNextCheckd', 64)
nextNextNextChecke = BitVec('nextNextNextChecke', 64)
finalChecka = BitVec('finalChecka', 64)
finalCheckb = BitVec('finalCheckb', 64)
finalCheckc = BitVec('finalCheckc', 64)
finalCheckd = BitVec('finalCheckd', 64)
finalChecke = BitVec('finalChecke', 64)
finalCheckf = BitVec('finalCheckf', 64)
finalCheckg = BitVec('finalCheckg', 64)
finalCheckh = BitVec('finalCheckh', 64)
vals = [maina, checka, keepgoinga, keepgoingb, checkthebigsa, checkthebigsb, nextNextNextChecka, nextNextNextCheckb,
nextNextNextCheckc, nextNextNextCheckd, nextNextNextChecke, finalChecka, finalCheckb, finalCheckc,
finalCheckd, finalChecke, finalCheckf, finalCheckg, finalCheckh]
s = Solver()
s.add(maina == 102)
s.add(checka & 1337 == 1057)
s.add(checka | 1337 == 28025)
s.add(checka ^ 1337 < 269700)
s.add(keepgoinga ^ keepgoingb == 28)
s.add(keepgoinga & keepgoingb == 99)
s.add(keepgoinga == 0x67)
s.add(keepgoingb == 0x7b)
s.add(checkthebigsb - checkthebigsa == 489139534831)
s.add(checkthebigsb & 4294967040 == 892362496)
s.add(checkthebigsb < checkthebigsa)
s.add((checkthebigsa & 0xFFFFFF00) == 0x52336300)
s.add(Or(checkthebigsa & 0xFF == ord('u'), checkthebigsa & 0xFF == ord('U')))
s.add(nextNextNextChecka - nextNextNextCheckb == -188178084847)
s.add(nextNextNextChecka * nextNextNextChecke == 10593957610752)
s.add((nextNextNextChecka - nextNextNextCheckb) + nextNextNextCheckc == 118730899270)
s.add(nextNextNextChecka + nextNextNextCheckb + nextNextNextCheckc + nextNextNextCheckd == 1346493052268)
s.add(nextNextNextCheckd | nextNextNextChecke == 409991082872)
s.add(nextNextNextCheckd - nextNextNextCheckc == 103082098739)
s.add(finalChecka == 30001)
s.add(finalCheckb == 25695)
s.add(finalCheckc == 26419)
s.add(finalCheckd == 928999216)
s.add(finalChecke == 62003745337707)
s.add(finalCheckf == 13151)
s.add(finalCheckg == 27955)
s.add(finalCheckh == 125)
s.check()
m = s.model()
print(m)
total_str = ""
for i in vals:
hex_str = hex(int(str(m[i])))[2:].replace("00", '')
cur_str = ""
for i in range(0, len(hex_str), 2):
try:
cur_str += bytes.fromhex(hex_str[i:i+2]).decode("ASCII")
except:
cur_str += "."
print(cur_str)
total_str += cur_str
print(total_str)
And with that you get:
f
la
g
{
~R3cU
.50_D
.3c0mp
._3z!_
Gu3s5
_u_sh
0
u1
d_
g3
7_g0
8d_71k
3_
m3
}
flag{~R3cU.50_D.3c0mp._3z!_Gu3s5_u_sh0u1d_g37_g08d_71k3_m3}
You know you may have sizes slightly off but if you constrain to only allow sizes you know for sure you end up with:
flag{R3cU.50_.3c0mp._3z!_Gu3s5_u_sh0u1d_g37_g08d_71k3_m3}
However, based on the challenge description you can see that they are either r/R or d/D for recurso and decompilation. Making the flag:
flag{R3cUr50_D3c0mp_3z!_Gu3s5_u_sh0u1d_g37_g08d_71k3_m3}